/***************************************************************************
 *   This is a part of the NextCAD Web APP.                                *
 *   Copyright (C) AMCAX, All rights reserved.                             *
 ***************************************************************************/

import {
  BaseCommand,
  CENTEREVENTS,
  CMEVENTS,
  pickedFaceData,
  pickedLineData,
  pickedVertexData,
} from '@amcax/base';
import { Picker3D, PickerMgr } from '@amcax/renderer';
import { eventCenterManager } from '@app-base/common/events/EventCenterManager';
import {
  NumberInputFormat,
  PickInputRJSFFormat,
} from '@app-cad/common/components/command-form/constants';
import { PickableCommandForm } from '@app-cad/common/components/command-form/pickable';
import { KEYBOARD_KEY } from '@app-cad/common/Constants';
import { useCommand } from '@app-cad/common/hooks/useCommand';
import { useTools } from '@app-cad/common/hooks/useTools';
import { MessageTip } from '@app-cad/common/utils/MessageTip';
import { previewer, userEvents } from '@app-cad/module';
import { wsApi } from '@app-cad/server';
import { FeaturedCmdDialogParams } from '@app-cad/types';
import { RJSFSchema } from '@rjsf/utils';
import { useAsyncEffect } from 'ahooks';
import i18n from 'i18next';
import { useEffect, useMemo, useState } from 'react';
import { useSwitchVisibility } from '../common/hooks/useSwitchVisibility';
import './index.scss';

type PickData = Array<(pickedLineData & pickedFaceData) & pickedVertexData>;

type FormData = {
  pickData?: PickData;
  radius1?: number; // 旋转轴方向，与 plane 平行
  // radius2?: number; // 旋转角度
};
type Params = {
  base: string;
  faces?: Array<object>;
  vertices?: Array<object>;
  edges?: Array<object>;
};

// 圆角
function FilletDialog({ title, onClose, props }: FeaturedCmdDialogParams) {
  const [confirmLoading, setConfirmLoading] = useState(false);
  const { picker } = useTools();

  useSwitchVisibility(props?.params?.base, props?.shape);

  useEffect(() => {
    eventCenterManager.activeEventCenter
      .setState('fillet')
      .on(CENTEREVENTS.KEYUP, onKeyUp)
      .addExit(exist);
    // 注册原始模型删除事件
    userEvents.on(CMEVENTS.MODEL_ADDED, newModelAdded);
    return () => {
      exist();
    };
  }, []);
  const [formData, setFormData] = useState<FormData>({
    pickData: [],
  });
  const getParams = () => {
    const params: Params = {
      base: props == null ? formData.pickData[0].idInfo.o : props.params.base,
      faces: [],
      edges: [],
      vertices: [],
    };
    formData.pickData.forEach((item) => {
      if (item.meshId) {
        params.faces.push({
          id: item.idInfo?.i,
          r1: formData.radius1,
          r2: formData.radius1,
        });
      }
      if (item.edgeId) {
        params.edges.push({
          id: item.idInfo?.i,
          r1: formData.radius1,
          r2: formData.radius1,
        });
      }
      if (item.vertexId) {
        params.vertices.push({
          id: item.idInfo?.i,
          r1: formData.radius1,
          r2: formData.radius1,
        });
      }
    });

    return params;
  };
  const validateParams = () => {
    if (
      formData.pickData?.length > 0 &&
      (formData.radius1 > 0 || typeof formData.radius1 === 'string')
    ) {
      return true;
    } else {
      return false;
    }
  };
  // 判断选择的面是否在同一个体上
  const isSameShell = (planes: PickData) => {
    const shellSet = new Set();
    for (let i = 0; i < planes.length; i++) {
      const modelDxid = planes[i].idInfo.o;
      shellSet.add(modelDxid);
    }
    return shellSet.size === 1;
  };
  useAsyncEffect(async () => {
    if (validateParams()) {
      const isSame = isSameShell(formData.pickData);
      if (!isSame) {
        MessageTip.error('选择的面必须在同一个体上');
        return;
      }
      let result;
      if (props == null) {
        result = await wsApi.createFillet(getParams(), true);
      } else {
        result = await wsApi.setParams(props.shape, getParams(), true);
      }
      previewInCommand(result);
    } else {
      previewer.clear();
    }
  }, [formData]);
  const finishValid = useMemo(validateParams, [formData]);
  useAsyncEffect(async () => {
    if (props == null) {
      return;
    }
    const { edges, faces, vertices } = props.params;
    const baseId = props.baseid;
    let radius1, dataObj;
    if (edges?.length > 0) {
      radius1 = edges[0].r1;
      for (const item of edges) {
        await initEditDependency([`${baseId}:e${item.id}`]);
      }
      dataObj = edges.map(
        (item) =>
          picker.generatePickObjByPickID(
            `${baseId}:e${item.id}`,
          ) as pickedLineData,
      );
    }
    if (faces?.length > 0) {
      radius1 = faces[0].r1;
      for (const item of faces) {
        await initEditDependency([`${baseId}:f${item.id}`]);
      }
      dataObj = faces.map(
        (item) =>
          picker.generatePickObjByPickID(
            `${baseId}:f${item.id}`,
          ) as pickedFaceData,
      );
    }
    if (vertices?.length > 0) {
      radius1 = vertices[0].r1;
      for (const item of vertices) {
        await initEditDependency([`${baseId}:v${item.id}`]);
      }
      dataObj = vertices.map(
        (item) =>
          picker.generatePickObjByPickID(
            `${baseId}:v${item.id}`,
          ) as pickedVertexData,
      );
    }
    setFormData({
      pickData: dataObj,
      radius1,
    });
  }, []);
  // 添加成功后删除原始模型
  const newModelAdded = () => {
    onClose();
  };

  const onKeyUp = (e) => {
    if (e.event.key === KEYBOARD_KEY.ESC) {
      exist();
      onClose();
    }
  };

  const handleExecute = async () => {
    setConfirmLoading(true);
    if (props == null) {
      await wsApi.createFillet(getParams(), false);
    } else {
      await wsApi.setParams(props.shape, getParams());
    }
    setConfirmLoading(false);
    previewer.clear();
  };

  const exist = () => {
    eventCenterManager.activeEventCenter
      .off(CENTEREVENTS.KEYUP, onKeyUp)
      .setDefault();

    userEvents.off(CMEVENTS.MODEL_ADDED, newModelAdded);
    PickerMgr.getPicker<Picker3D>('3d').init();
  };
  const schema: RJSFSchema = {
    type: 'object',
    required: ['line', 'radius1'],
    properties: {
      pickData: {
        type: 'array',
        items: {
          type: 'string',
          enum: [],
        },
        uniqueItems: true,
        title: '请选择点/线/面',
        format: PickInputRJSFFormat.MultiPickALL,
      },
      radius1: {
        type: 'number',
        title: '圆角半径',
        default: 2,
        minimum: 0.01,
        format: NumberInputFormat.Length,
      },
    },
  };

  const checkRadius = (radius: number) => {
    return radius != null && radius !== 0;
  };
  const checkAndFinishDraw = async () => {
    const { pickData, radius1 } = formData;
    if (pickData == null || !checkRadius(radius1)) {
      return;
    }
    await handleExecute();
    previewer.clear();
    onClose();
  };
  const { initEditDependency, initingEdit, previewInCommand } = useCommand(
    'cmd-shell',
    {
      onClose,
      finishDraw: checkAndFinishDraw,
    },
  );
  return (
    <PickableCommandForm
      isFormDataValid={finishValid}
      onClose={() => {
        previewer.clear();
        onClose();
      }}
      disabled={initingEdit}
      title={title}
      schema={schema}
      formData={formData}
      onChange={setFormData}
      extraUiSchema={{
        'ui:options': {
          classNames: 'command-form command-fillet',
        },
        pickData: {
          'ui:placeholder': '请输入点/线/面',
          'ui:autofocus': true,
        },
      }}
      submitLoading={confirmLoading}
      submitText='完成'
      onSubmit={checkAndFinishDraw}
    />
  );
}

export const cmdFillet: BaseCommand = {
  id: 'CmdFillet',
  label: 'command.fillet',
  icon: 'commands/feature/fillet',
  create: (label: string, onClose: () => void, props) => (
    <FilletDialog
      title={i18n.t(label)}
      onClose={onClose}
      props={props}
    />
  ),
};
